package com.prd.service.Impl;

import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.lang.generator.SnowflakeGenerator;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.prd.config.IdGeneratorSnowflake;
import com.prd.config.LoginInterceptor;
import com.prd.dto.SysRoleDTO;
import com.prd.dto.SysUserDTO;
import com.prd.dto.SysUserRoleDTO;
import com.prd.enums.SysIsDeleteTyPeEnum;
import com.prd.mapper.SysRoleMenuMapper;
import com.prd.mapper.SysUserRoleMapper;
import com.prd.model.LoginUser;
import com.prd.model.SysRoleDO;
import com.prd.mapper.SysRoleMapper;
import com.prd.model.SysRoleMenuDO;
import com.prd.model.SysUserRoleDO;
import com.prd.service.SysRoleService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.prd.util.CommonUtil;
import com.prd.util.JsonData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * <p>
 * 角色数据表 服务实现类
 * </p>
 *
 * @author lizhaoyi
 * @since 2024-01-09
 */
@Service
public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRoleDO> implements SysRoleService {

    @Autowired
    private SysRoleMapper sysRoleMapper;

    @Autowired
    private SysUserRoleMapper sysUserRoleMapper;

    @Autowired
    private SysRoleMenuMapper sysRoleMenuMapper;

    @Autowired
    private IdGeneratorSnowflake idGeneratorSnowflake;


    /**
     * 根据条件分页查询角色数据
     *
     * @param roleDTO
     * @return
     */
    @Override
    public List<SysRoleDTO> selectRoleList(SysRoleDTO roleDTO) {
        List<SysRoleDO> sysRoleDOS = sysRoleMapper.selectRoleList(CommonUtil.dtoTransfer(roleDTO, SysRoleDO.class));
        return CommonUtil.dtoListTransfer(sysRoleDOS,SysRoleDTO.class);
    }

    /**
     * 新增角色
     *
     * @param roleDTO
     * @return
     */
    @Override
    @Transactional
    public JsonData insertRole(SysRoleDTO roleDTO) {

        LoginUser loginUser = LoginInterceptor.threadLocal.get();
        long id = idGeneratorSnowflake.snowflakeId();
        roleDTO.setId(id);
        if (checkRoleNameUnique(roleDTO)) {
            return JsonData.buildError("新增角色'" + roleDTO.getName() + "'失败，角色名称已存在");
        }
        if (loginUser == null) {
            roleDTO.setCreator("system");
        }else {
            roleDTO.setCreator(loginUser.getName());
        }
        roleDTO.setCreateTime(LocalDateTime.now());
        roleDTO.setDeleted(SysIsDeleteTyPeEnum.UN_DELETE.getCode());
        sysRoleMapper.insert(CommonUtil.dtoTransfer(roleDTO, SysRoleDO.class));

        //新增角色菜单
        insertRoleMenu(roleDTO);
        return JsonData.buildSuccess();

    }


    /**
     * 修改保存角色
     *
     * @param roleDTO
     * @return
     */
    @Override
    public JsonData updateRole(SysRoleDTO roleDTO) {
        //校验角色是否允许操作
        if (roleDTO.isAdmin()) {
            return JsonData.buildError("不允许操作超级管理员角色");
        }
        if (checkRoleNameUnique(roleDTO)) {
            return JsonData.buildError("修改角色'" + roleDTO.getName() + "'失败，角色名称已存在");
        }
        roleDTO.setUpdater(LoginInterceptor.threadLocal.get().getName());
        roleDTO.setUpdateTime(LocalDateTime.now());
        sysRoleMapper.updateById(CommonUtil.dtoTransfer(roleDTO, SysRoleDO.class));
        //删除角色与菜单关联
        sysRoleMenuMapper.delete(new LambdaQueryWrapper<SysRoleMenuDO>().eq(SysRoleMenuDO::getRoleId, roleDTO.getId()));
        //新建角色与菜单关联
        insertRoleMenu(roleDTO);

        return JsonData.buildSuccess();
    }

    /**
     * 删除角色
     *
     * @param roleIds
     * @return
     */
    @Override
    public JsonData deleteRoleByIds(Long[] roleIds) {

        for (Long roleId : roleIds) {
            if (new SysRoleDTO(roleId).isAdmin()) {
                return JsonData.buildError("不允许操作超级管理员角色");
            }
            if (checkIsUse(roleId)) {
                return JsonData.buildError("角色已分配,不能删除");
            }
        }
        sysRoleMapper.deleteBatchIds(Arrays.asList(roleIds));

        //删除角色与菜单关联
        sysRoleMenuMapper.delete(new LambdaQueryWrapper<SysRoleMenuDO>().in(SysRoleMenuDO::getRoleId, Arrays.asList(roleIds)));

        return JsonData.buildSuccess();
    }

    /**
     * 根据角色id获取详细信息
     *
     * @param roleId
     * @return
     */
    @Override
    public JsonData selectRoleById(Long roleId) {

        return JsonData.buildSuccess(sysRoleMapper.selectById(roleId));
    }

    /**
     * 取消授权用户
     *
     * @param sysUserRoleDTO
     * @return
     */
    @Override
    public JsonData deleteAuthUser(SysUserRoleDTO sysUserRoleDTO) {

        return JsonData.buildSuccess(sysUserRoleMapper.delete(new LambdaQueryWrapper<SysUserRoleDO>()
                .eq(SysUserRoleDO::getUserId, sysUserRoleDTO.getUserId())
                .eq(SysUserRoleDO::getRoleId, sysUserRoleDTO.getRoleId())));
    }

    /**
     * 批量取消授权用户
     *
     * @param roleId
     * @param userIds
     * @return
     */
    @Override
    public JsonData deleteAuthUsers(Long roleId, Long[] userIds) {

        return JsonData.buildSuccess(sysUserRoleMapper.delete(new LambdaQueryWrapper<SysUserRoleDO>()
                .eq(SysUserRoleDO::getRoleId, roleId)
                .in(SysUserRoleDO::getUserId, Arrays.asList(userIds))));
    }

    /**
     * 批量选择用户授权
     *
     * @param roleId
     * @param userIds
     * @return
     */
    @Override
    public JsonData insertAuthUsers(Long roleId, Long[] userIds) {
        if (!checkRoleDataScope(roleId)) {
            return JsonData.buildError("没有权限访问角色数据");
        }
        // 新增用户与角色管理
        List<SysUserRoleDO> list = new ArrayList<>();
        for (Long userId : userIds) {
            SysUserRoleDO sysUserRoleDO = new SysUserRoleDO();
            sysUserRoleDO.setRoleId(roleId);
            sysUserRoleDO.setUserId(userId);
            list.add(sysUserRoleDO);
        }
        return JsonData.buildSuccess(sysUserRoleMapper.batchUserRole(list));
    }

    /**
     * 根据用户ID查询角色列表
     * @param userId
     * @return
     */
    @Override
    public List<SysRoleDTO> selectRoleByUserId(Long userId) {

        return sysRoleMapper.selectRoleByUserId(userId);
    }


    /**
     * 校验角色是否被分配
     *
     * @param roleId
     * @return
     */
    private boolean checkIsUse(Long roleId) {

        Integer count = sysUserRoleMapper.selectCount(new LambdaQueryWrapper<SysUserRoleDO>().eq(SysUserRoleDO::getRoleId, roleId));
        if (count > 0) {
            return true;
        }
        return false;
    }

    /**
     * 校验菜单名称是否唯一
     *
     * @param roleDTO
     * @return
     */
    private boolean checkRoleNameUnique(SysRoleDTO roleDTO) {

        SysRoleDO sysRoleDO = sysRoleMapper.selectOne(new LambdaQueryWrapper<SysRoleDO>()
                .eq(SysRoleDO::getName, roleDTO.getName())
                .eq(SysRoleDO::getDeleted, SysIsDeleteTyPeEnum.UN_DELETE.getCode()));

        return sysRoleDO != null;
    }

    /**
     * 新增角色菜单
     *
     * @param roleDTO
     */
    private void insertRoleMenu(SysRoleDTO roleDTO) {
        List<SysRoleMenuDO> list = new ArrayList<>();
        for (Long menuId : roleDTO.getMenuIds()) {
            SysRoleMenuDO sysRoleMenuDO = new SysRoleMenuDO();
            sysRoleMenuDO.setRoleId(roleDTO.getId());
            sysRoleMenuDO.setMenuId(menuId);
            list.add(sysRoleMenuDO);
        }
        if (!CollectionUtils.isEmpty(list)) {
            sysRoleMenuMapper.batchRoleMenu(list);
        }
    }

    /**
     * 校验角色是否有数据权限
     *
     * @param roleId
     */
    public boolean checkRoleDataScope(Long roleId) {

        if (!SysUserDTO.isAdmin(LoginInterceptor.threadLocal.get().getId())) {
            SysRoleDTO sysRoleDTO = new SysRoleDTO();
            sysRoleDTO.setId(roleId);
            List<SysRoleDO> sysRoleDOS = sysRoleMapper.selectRoleList(CommonUtil.dtoTransfer(sysRoleDTO, SysRoleDO.class));


            if (CollectionUtils.isEmpty(sysRoleDOS)) {
                return false;
            }
        }
        return true;
    }

}
